/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.bff.gaia.mix.api.datastream;

import com.bff.gaia.annotation.PublicEvolving;
import com.bff.gaia.api.common.typeinfo.TypeInformation;
import com.bff.gaia.api.java.tuple.Tuple;
import com.bff.gaia.api.java.tuple.Tuple1;
import com.bff.gaia.api.java.tuple.Tuple10;
import com.bff.gaia.api.java.tuple.Tuple11;
import com.bff.gaia.api.java.tuple.Tuple12;
import com.bff.gaia.api.java.tuple.Tuple13;
import com.bff.gaia.api.java.tuple.Tuple14;
import com.bff.gaia.api.java.tuple.Tuple15;
import com.bff.gaia.api.java.tuple.Tuple16;
import com.bff.gaia.api.java.tuple.Tuple17;
import com.bff.gaia.api.java.tuple.Tuple18;
import com.bff.gaia.api.java.tuple.Tuple19;
import com.bff.gaia.api.java.tuple.Tuple2;
import com.bff.gaia.api.java.tuple.Tuple20;
import com.bff.gaia.api.java.tuple.Tuple21;
import com.bff.gaia.api.java.tuple.Tuple22;
import com.bff.gaia.api.java.tuple.Tuple23;
import com.bff.gaia.api.java.tuple.Tuple24;
import com.bff.gaia.api.java.tuple.Tuple25;
import com.bff.gaia.api.java.tuple.Tuple3;
import com.bff.gaia.api.java.tuple.Tuple4;
import com.bff.gaia.api.java.tuple.Tuple5;
import com.bff.gaia.api.java.tuple.Tuple6;
import com.bff.gaia.api.java.tuple.Tuple7;
import com.bff.gaia.api.java.tuple.Tuple8;
import com.bff.gaia.api.java.tuple.Tuple9;
import com.bff.gaia.api.java.typeutils.TupleTypeInfo;
import com.bff.gaia.mix.api.operators.MixStreamProject;
import com.bff.gaia.util.Preconditions;

/**
 * The result of {@link DataMixStream#project(int...)}. This can be used to add more fields to the
 * projection.
 */
@PublicEvolving
public class MixStreamProjection<IN> {

	private DataMixStream<IN> dataMixStream;
	private int[] fieldIndexes;

	protected MixStreamProjection(DataMixStream<IN> dataMixStream, int[] fieldIndexes) {
		if (!dataMixStream.getType().isTupleType()) {
			throw new RuntimeException("Only Tuple DataMixStreams can be projected");
		}
		if (fieldIndexes.length == 0) {
			throw new IllegalArgumentException("project() needs to select at least one (1) field.");
		} else if (fieldIndexes.length > Tuple.MAX_ARITY - 1) {
			throw new IllegalArgumentException(
					"project() may select only up to (" + (Tuple.MAX_ARITY - 1) + ") fields.");
		}

		int maxFieldIndex = (dataMixStream.getType()).getArity();
		for (int i = 0; i < fieldIndexes.length; i++) {
			Preconditions.checkElementIndex(fieldIndexes[i], maxFieldIndex);
		}

		this.dataMixStream = dataMixStream;
		this.fieldIndexes = fieldIndexes;
	}

	/**
	 * Chooses a projectTupleX according to the length of
	 * {@link com.bff.gaia.mix.api.datastream.MixStreamProjection#fieldIndexes}.
	 *
	 * @return The projected DataMixStream.
	 * @see com.bff.gaia.api.java.operators.ProjectOperator.Projection
	 */
	@SuppressWarnings("unchecked")
	public <OUT extends Tuple> SingleOutputMixStreamOperator<OUT> projectTupleX() {
		SingleOutputMixStreamOperator<OUT> projOperator = null;

		switch (fieldIndexes.length) {
			case 1: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple1(); break;
			case 2: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple2(); break;
			case 3: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple3(); break;
			case 4: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple4(); break;
			case 5: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple5(); break;
			case 6: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple6(); break;
			case 7: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple7(); break;
			case 8: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple8(); break;
			case 9: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple9(); break;
			case 10: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple10(); break;
			case 11: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple11(); break;
			case 12: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple12(); break;
			case 13: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple13(); break;
			case 14: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple14(); break;
			case 15: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple15(); break;
			case 16: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple16(); break;
			case 17: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple17(); break;
			case 18: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple18(); break;
			case 19: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple19(); break;
			case 20: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple20(); break;
			case 21: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple21(); break;
			case 22: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple22(); break;
			case 23: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple23(); break;
			case 24: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple24(); break;
			case 25: projOperator = (SingleOutputMixStreamOperator<OUT>) projectTuple25(); break;
			default:
				throw new IllegalStateException("Excessive arity in tuple.");
		}

		return projOperator;
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0> SingleOutputMixStreamOperator<Tuple1<T0>> projectTuple1() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple1<T0>> tType = new TupleTypeInfo<Tuple1<T0>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple1<T0>>(
				fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1> SingleOutputMixStreamOperator<Tuple2<T0, T1>> projectTuple2() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple2<T0, T1>> tType = new TupleTypeInfo<Tuple2<T0, T1>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple2<T0, T1>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2> SingleOutputMixStreamOperator<Tuple3<T0, T1, T2>> projectTuple3() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple3<T0, T1, T2>> tType = new TupleTypeInfo<Tuple3<T0, T1, T2>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple3<T0, T1, T2>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3> SingleOutputMixStreamOperator<Tuple4<T0, T1, T2, T3>> projectTuple4() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple4<T0, T1, T2, T3>> tType = new TupleTypeInfo<Tuple4<T0, T1, T2, T3>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple4<T0, T1, T2, T3>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4> SingleOutputMixStreamOperator<Tuple5<T0, T1, T2, T3, T4>> projectTuple5() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple5<T0, T1, T2, T3, T4>> tType = new TupleTypeInfo<Tuple5<T0, T1, T2, T3, T4>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple5<T0, T1, T2, T3, T4>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5> SingleOutputMixStreamOperator<Tuple6<T0, T1, T2, T3, T4, T5>> projectTuple6() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple6<T0, T1, T2, T3, T4, T5>> tType = new TupleTypeInfo<Tuple6<T0, T1, T2, T3, T4, T5>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple6<T0, T1, T2, T3, T4, T5>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6> SingleOutputMixStreamOperator<Tuple7<T0, T1, T2, T3, T4, T5, T6>> projectTuple7() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple7<T0, T1, T2, T3, T4, T5, T6>> tType = new TupleTypeInfo<Tuple7<T0, T1, T2, T3, T4, T5, T6>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple7<T0, T1, T2, T3, T4, T5, T6>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7> SingleOutputMixStreamOperator<Tuple8<T0, T1, T2, T3, T4, T5, T6, T7>> projectTuple8() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple8<T0, T1, T2, T3, T4, T5, T6, T7>> tType = new TupleTypeInfo<Tuple8<T0, T1, T2, T3, T4, T5, T6, T7>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple8<T0, T1, T2, T3, T4, T5, T6, T7>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8> SingleOutputMixStreamOperator<Tuple9<T0, T1, T2, T3, T4, T5, T6, T7, T8>> projectTuple9() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple9<T0, T1, T2, T3, T4, T5, T6, T7, T8>> tType = new TupleTypeInfo<Tuple9<T0, T1, T2, T3, T4, T5, T6, T7, T8>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple9<T0, T1, T2, T3, T4, T5, T6, T7, T8>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9> SingleOutputMixStreamOperator<Tuple10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>> projectTuple10() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>> tType = new TupleTypeInfo<Tuple10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple10<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> SingleOutputMixStreamOperator<Tuple11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>> projectTuple11() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>> tType = new TupleTypeInfo<Tuple11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple11<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> SingleOutputMixStreamOperator<Tuple12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>> projectTuple12() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>> tType = new TupleTypeInfo<Tuple12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple12<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> SingleOutputMixStreamOperator<Tuple13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>> projectTuple13() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>> tType = new TupleTypeInfo<Tuple13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple13<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> SingleOutputMixStreamOperator<Tuple14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>> projectTuple14() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>> tType = new TupleTypeInfo<Tuple14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple14<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> SingleOutputMixStreamOperator<Tuple15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>> projectTuple15() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>> tType = new TupleTypeInfo<Tuple15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple15<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> SingleOutputMixStreamOperator<Tuple16<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>> projectTuple16() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple16<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>> tType = new TupleTypeInfo<Tuple16<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple16<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> SingleOutputMixStreamOperator<Tuple17<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>> projectTuple17() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple17<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>> tType = new TupleTypeInfo<Tuple17<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple17<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17> SingleOutputMixStreamOperator<Tuple18<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>> projectTuple18() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple18<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>> tType = new TupleTypeInfo<Tuple18<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple18<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18> SingleOutputMixStreamOperator<Tuple19<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>> projectTuple19() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple19<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>> tType = new TupleTypeInfo<Tuple19<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple19<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19> SingleOutputMixStreamOperator<Tuple20<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>> projectTuple20() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple20<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>> tType = new TupleTypeInfo<Tuple20<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple20<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20> SingleOutputMixStreamOperator<Tuple21<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>> projectTuple21() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple21<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>> tType = new TupleTypeInfo<Tuple21<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple21<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21> SingleOutputMixStreamOperator<Tuple22<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>> projectTuple22() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple22<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>> tType = new TupleTypeInfo<Tuple22<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple22<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22> SingleOutputMixStreamOperator<Tuple23<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>> projectTuple23() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple23<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>> tType = new TupleTypeInfo<Tuple23<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple23<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23> SingleOutputMixStreamOperator<Tuple24<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>> projectTuple24() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple24<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>> tType = new TupleTypeInfo<Tuple24<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple24<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	/**
	 * Projects a {@link Tuple} {@link DataMixStream} to the previously selected fields.
	 *
	 * @return The projected DataMixStream.
	 * @see Tuple
	 * @see DataMixStream
	 */
	public <T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24> SingleOutputMixStreamOperator<Tuple25<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>> projectTuple25() {
		TypeInformation<?>[] fTypes = extractFieldTypes(fieldIndexes, dataMixStream.getType());
		TupleTypeInfo<Tuple25<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>> tType = new TupleTypeInfo<Tuple25<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>>(fTypes);

		return dataMixStream.transform("Projection", tType, new MixStreamProject<IN, Tuple25<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21, T22, T23, T24>>(fieldIndexes, tType.createSerializer(dataMixStream.getExecutionConfig())));
	}

	public static TypeInformation<?>[] extractFieldTypes(int[] fields, TypeInformation<?> inType) {

		TupleTypeInfo<?> inTupleType = (TupleTypeInfo<?>) inType;
		TypeInformation<?>[] fieldTypes = new TypeInformation[fields.length];

		for (int i = 0; i < fields.length; i++) {
			fieldTypes[i] = inTupleType.getTypeAt(fields[i]);
		}

		return fieldTypes;
	}

}